package com.hushan.elevator.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.lang.Validator;
import cn.hutool.core.text.PasswdStrength;
import cn.hutool.jwt.JWT;
import com.hushan.elevator.dao.UserDeptRepository;
import com.hushan.elevator.dao.UserRepository;
import com.hushan.elevator.dao.UserRoleRepository;
import com.hushan.elevator.dto.UserAddDto;
import com.hushan.elevator.model.*;
import com.hushan.elevator.service.UserService;

import com.hushan.elevator.utils.exception.UserException;
import com.hushan.elevator.utils.exception.UserLoginException;
import jakarta.transaction.Transactional;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.util.List;

/**
 * @Author: zzy
 * @Date: 2024/10/22 22:50
 * @Title:
 * @Version:
 * @Package: com.hushan.elevator.service.impl
 */

@Slf4j
@Service
public class UserServiceImpl implements UserService {
    @Value("${spring.security.jwt-sign-key}")
    String JWT_SIGN_KEY; // jwt key
    @Value("${spring.security.pwd-strength}")
    int PWD_STRENGTH;


    private final UserRepository userRepository;
    private final PasswordEncoder passwordEncoder;
    private final UserRoleRepository userRoleDao;
    private final UserDeptRepository userDeptDao;
    @Autowired
    public UserServiceImpl(UserRepository userRepository, PasswordEncoder passwordEncoder, UserRoleRepository userRoleDao, UserDeptRepository userDeptDao) {
        this.userRepository = userRepository;
        this.passwordEncoder = passwordEncoder;
        this.userRoleDao = userRoleDao;
        this.userDeptDao = userDeptDao;
    }


    @Override
    public User findUserByUserId(Integer userId) {
        return userRepository.findByUserId(userId).orElse(null);
    }

    @Override
    public User findUserByTelNumb(String telNumb) {
        return userRepository.findByTelNumb(telNumb).orElseThrow(() -> new UsernameNotFoundException("User not found"));
    }

    @Override
    public User findUserByEmail(String email) {
        return userRepository.findByEmail(email).stream().findFirst().orElse(null);
    }



    @Override
    @Transactional
    public void deleteUser(User user) {
        user.setIsDeleted(true);
        userRepository.save(user);
    }

    @Override
    @Transactional
    public void updateUser(User user) throws UserException {
        // Check if user exists before updating
        User oldInfo = userRepository.findByUserId(user.getUserId()).orElse(null);
        if (oldInfo==null) {
            throw  new UserException("用户不存在");
        }
        oldInfo.setEmail(user.getEmail());
        oldInfo.setTelNumb(user.getTelNumb());
        oldInfo.setSex(user.getSex());
        oldInfo.setTitle(user.getTitle());
        oldInfo.setUsername(user.getUsername());
        userRepository.save(oldInfo);
    }

    @Override
    public void changeUserEnableStatus(Integer userId, Boolean newStatus) {
        User user = userRepository.findByUserId(userId).orElseThrow(() -> new RuntimeException("User not found with id " + userId));
        user.setEnable(newStatus);
        userRepository.save(user);
    }

    /**
     * Add user to database.
     * <p>
     * Steps:
     * <ul>
     *     <li>1. Verify user is existed</li>
     *     <li>2. Email is valid</li>
     *     <li>3. Password is strong
     *         <ul>
     *             <li>Password length > 6</li>
     *             <li>Password type > 2</li>
     *             <li>Example: Password: q123456</li>
     *         </ul>
     *     </li>
     * </ul>
     * @param dto User data
     * @throws UserException
     *          <p>User already exists</p>
     *          <p>email invalid</p>
     *          <p>password easy</p>
     */
    @Override
    @Transactional
    public void addUser(UserAddDto dto) throws UserException {
        User r = userRepository.findByTelNumb(dto.getTelNumb()).orElse(null);
        if (r!=null){
            throw new UserException("user already exists");
        }
        if(!Validator.isEmail(dto.getEmail())){
            throw new UserException("email invalid");
        }
        if (PasswdStrength.check(dto.getPassword())<PWD_STRENGTH){
            throw new UserException("密码过于简单");
        }
        // save user
        User user = new User();
        BeanUtil.copyProperties(dto, user);
        user.setPassword(passwordEncoder.encode(user.getPassword()));
        user.setLatestTime(LocalDateTime.now());
        user.setEnable(false);
        userRepository.save(user);
    }

    @Override
    public void changePwd(User user, String pwd) throws UserException {
        if (PasswdStrength.check(pwd)<PWD_STRENGTH){
            throw new UserException("密码过于简单");
        }
        user.setPassword(passwordEncoder.encode(pwd));
        userRepository.save(user);
    }

    /***
     * verify password is correct
     * @param username your username
     * @param password your password
     * @return is correct
     * @throws UserLoginException User not found, Password error
     */
    @Override
    public boolean verifyUser(User user,String username, String password) throws UserLoginException {
        if (user == null) {
            throw new UserLoginException("用户不存在");
        }
        if (!passwordEncoder.matches(password,user.getPassword())){
            throw new UserLoginException("密码错误");
        }
        return true;
    }

    @Override
    public String authentication(String username, String password) {
        return JWT.create()
                .setPayload("username", username)
                .setKey(JWT_SIGN_KEY.getBytes(StandardCharsets.UTF_8))
                .sign();
    }

    @Override
    public void addUserDeptConnect(int userId, int deptId, int createdBy) {
        UserDeptPK userDeptPK = new UserDeptPK(userId,deptId);
        UserDept userDept = new UserDept(userDeptPK,createdBy,LocalDateTime.now(),LocalDateTime.now());
        userDeptDao.save(userDept);
    }

    @Override
    public void addUserRoleConnect(int userId, int roleId, int createdBy) {
        UserRolePK userRolePK = new UserRolePK(userId,roleId);
        UserRole userRole = new UserRole(userRolePK,createdBy,LocalDateTime.now(),LocalDateTime.now());
        userRoleDao.save(userRole);
    }

    @Override
    public List<User> findUsersByDept(Integer deptId) {
        return userRepository.findUsersByDept(deptId);
    }

    @Override
    public int findUserCountByDept(int id) {
        return userRepository.findCountByDept(id);
    }

    @Override
    public void updateRole(Integer userId, Integer oldRoleId,Integer newRoleId) {

        userRoleDao.updateUserRole(userId,oldRoleId,newRoleId);
    }

    @Override
    public void updateDept(Integer userId, Integer oldDeptId,Integer newDeptId) {
        userDeptDao.updateUserDept(userId,oldDeptId,newDeptId);
    }

    @Override
    public void deleteUsers(List<User> usersByDept) {
        for (User user : usersByDept) {
            deleteUser(user);
        }
    }

}
